/*
 * Famon (http://code.google.com/p/famon/)
 * Copyright (c) 2010 Carl Crous.
 *
 * This file is part of Famon.
 *
 * Famon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Famon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Famon.  If not, see <http://www.gnu.org/licenses/>.
 */

package za.org.foobar.famon.frontend.server;

import com.google.appengine.api.datastore.Text;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gwtportlets.portlet.client.WidgetFactory;
import org.gwtportlets.portlet.server.PageProvider;
import org.gwtportlets.portlet.server.PageRequest;
import org.gwtportlets.portlet.server.WidgetFactoryXmlIO;
import za.org.foobar.famon.frontend.client.FamonException;
import za.org.foobar.famon.frontend.client.ui.*;
import za.org.foobar.famon.model.Page;
import za.org.foobar.famon.util.DB;

import java.io.*;


/**
 * Loads our pages to/from XML files. This implementation checks for the
 * existance of a $user.dir/web/WEB-INF/pages directory. If this is found then
 * pages are loaded and saved to files under that directory. Otherwise pages are
 * read from the session and WEB-INF/pages with changes stored in the session.
 *
 * This means that someone running the famon on their own machine and starting
 * Tomcat from the project root directory will be able to make persistent
 * changes to pages.
 */
public class FamonPageProvider extends PageProvider {

    private final Log log = LogFactory.getLog(getClass());

    private static FamonPageProvider instance;
    public static FamonPageProvider get() {
        return instance == null ? instance = new FamonPageProvider() : instance;
    }

//    private Map<Class, Class> providerClassMap = new HashMap<Class, Class>();
    private File pageDir;
    private WidgetFactoryXmlIO xmlIO = new WidgetFactoryXmlIO();

    public FamonPageProvider() {
        pageDir = new File("WEB-INF/pages");
        log.debug("Loading physical pages from: " + pageDir.getAbsolutePath());
        add(new ApplicationManagerDataProvider());
        add(new FailureBrowserDataProvider());
        add(new PageEditorDataProvider());
        add(new PropertyDataProvider());
        add(new LoginDataProvider());
        add(new FailureDataProvider());
        xmlIO.alias(ApplicationManagerPortlet.Factory.class);
        xmlIO.alias(FailureBrowserPortlet.Factory.class);
        xmlIO.alias(PageEditorPortlet.Factory.class);
        xmlIO.alias(PropertyPortlet.Factory.class);
        xmlIO.alias(LoginPortlet.Factory.class);
        xmlIO.alias(FailurePortlet.Factory.class);
    }

//    @Override
//    public void add(WidgetDataProvider p) {
//        p.getClass()
//    }
//
//    @Override
//    public WidgetDataProvider findWidgetDataProvider(WidgetFactory wf) {
//        return ;
//    }

    //    @Autowired
//    public void setWidgetDataProviders(WidgetDataProvider[] a) {
//        for (int i = a.length - 1; i >= 0; i--) {
//            WidgetDataProvider p = a[i];
//            add(p);
//            if (log.isDebugEnabled()) {
//                log.debug("Add DataProvider: " + p.getWidgetFactoryClass().getName() + " -> " + p.getClass().toString());
//            }
//        }
////        if (log.isDebugEnabled()) {
////            List<Class> keys = new ArrayList<Class>(providerMap.keySet());
////            Collections.sort(keys, new Comparator<Class>() {
////                public int compare(Class o1, Class o2) {
////                    return o1.getName().compareTo(o2.getName());
////                }
////            });
////            for (int i = 0; i < keys.size(); i++) {
////                Class key = keys.get(i);
////                log.debug(key.getName() + " -> " + providerMap.get(key));
////            }
////        }
//    }

//    @Autowired
//    public void setPortletFactoryList(PortletFactory[] list) {
//        for (int i = list.length - 1; i >= 0; i--) {
//            Class cls = list[i].getClass();
//            xmlIO.alias(cls);
//            if (log.isDebugEnabled()) {
//                log.debug("Add Portlet Factory: " + cls.getName());
//            }
//        }
//    }

    protected WidgetFactory createWidgetFactory(PageRequest req) {
        String data = getPageXML(req);
        InputStream in = null;
        WidgetFactory wf;
        try {
            if (data != null) {
                try {
                    in = new ByteArrayInputStream(data.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    throw new FamonException("Unsupported encoding");
                }
            } else {
                try {
                    in = openPageStream(req);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (in == null) {
                return null;
            }
            wf = xmlIO.fromXML(in);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ignored) {
                }
            }
        }
        return wf;
    }

//    public WidgetDataProvider findWidgetDataProvider(WidgetFactory wf) {
//        WidgetDataProvider ans = providerMap.get(wf.getClass());
//        if (log.isDebugEnabled()) {
//            log.debug(wf.getClass() + " -> " + ans);
//        }
//        return ans;
//    }

    protected void handleRefreshException(PageRequest req, WidgetFactory top,
            WidgetFactory wf, Exception e) {
        if (e instanceof RuntimeException) {
            throw ((RuntimeException)e);
        }
        throw new RuntimeException(e.toString(), e);
    }

    /**
     * Update the page layout.
     */
    public void savePage(String pageName, WidgetFactory wf) {
        Page page = DB.findByName(Page.class, pageName);
        DB.begin();
        if (page == null) {
            page = new Page(pageName);
            DB.persist(page);
        } else {
            DB.merge(page);
        }

        page.setContents(new Text(xmlIO.toXML(wf)));
        DB.commit();
    }

    /**
     * Get the XML for the page or null if page is not found.
     */
    public String getPageXML(PageRequest req) {
        Page page = DB.findByName(Page.class, req.getPageName());
        if (page == null || page.getContents() == null) {
            return null;
        }
        return page.getContents().getValue();
    }

    private InputStream openPageStream(PageRequest req) throws IOException {
        String filename = req.getPageName() + ".xml";
        if (pageDir == null) {
            log.error("No page dir set, cannot load from filesystem");
            return null;
        }
        File f = new File(pageDir, filename);
        if (!f.isFile()) {
            return null;
        }
        return new FileInputStream(f);
    }

}

